package jp.saver.speedtest;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.DecimalFormat;
import jp.saver.speedtest.R;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class SpeedTestLauncher extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		mDecimalFormater = new DecimalFormat("##.##");
		// Request the progress bar to be shown in the title
		// requestWindowFeature(Window.FEATURE_PROGRESS);
		setContentView(R.layout.main);
		bindListeners();
	}

	// Setup event handlers
	private void bindListeners() {
		mBtnStart = (Button) findViewById(R.id.btnStart);
		mTxtSpeed = (TextView) findViewById(R.id.speed);
		//mTxtConnectionSpeed = (TextView) findViewById(R.id.connectionspeeed);
		mTxtUploadSpeed = (TextView) findViewById(R.id.uploadspeed);
		mTxtProgress = (TextView) findViewById(R.id.progress);
		mTxtNetwork = (TextView) findViewById(R.id.networktype);
		mProgressBar = (ProgressBar) findViewById(R.id.progressBar);

		// get a reference to the ImageView component that will display the image:
		imgPointer = (ImageView) findViewById(R.id.pointer);

		// load a resource from the res/drawable directory:
		myBitmap = BitmapFactory.decodeResource(getResources(),
				R.drawable.pointer);
		
		pointerW = myBitmap.getWidth();
		pointerH =  myBitmap.getHeight();

//		// find the width and height of the screen:
//		Display d = getWindowManager().getDefaultDisplay();
//		int x = d.getWidth();
//		int y = d.getHeight();
//
//		// scale it to fit the screen, x and y swapped because my image is wider than it is tall
//		Bitmap scaledBitmap = Bitmap.createScaledBitmap(myBitmap, y, x, true);

		setPointer(ZERO_POINT_DEGREE);

		mBtnStart.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(final View view) {
				if (Connectivity.isConnected(getApplicationContext())) {
					// setProgressBarVisibility(true);
					mTestType = TYPE_DOWNLOAD;
					mBtnStart.setText("Downloading test...");
					mTxtUploadSpeed.setText("Upload speed: ");
					mTxtProgress.setText(String.format(getResources()
							.getString(R.string.update_progress), 0)
							+ "%");
					mBtnStart.setEnabled(false);
					mTxtNetwork.setText(R.string.network_detecting);
					new Thread(mWorker).start();
				} else {
					Toast.makeText(getApplicationContext(), "No connection",
							Toast.LENGTH_LONG).show();
				}
			}
		});
	}

	private final Handler mHandler = new Handler() {
		@Override
		public void handleMessage(final Message msg) {
			switch (msg.what) {
			
			case MSG_UPDATE_STATUS:
				final SpeedInfo info1 = (SpeedInfo) msg.obj;
				if (mTestType == TYPE_DOWNLOAD) {
					mTxtSpeed
					.setText("Download speed: "
							+ String.format(getResources().getString(R.string.update_speed),
									mDecimalFormater.format(info1.kilobits)));
				} else {
					mTxtUploadSpeed.setText("Upload speed: "
							+ mDecimalFormater.format(info1.kilobits) 
							+ " kbit/sec");
				}

				mProgressBar.setProgress(msg.arg1);
				// mTxtProgrNum.setText(String.format("%d", msg.arg1) + "%");
				mTxtProgress.setText(String.format(
						getResources().getString(R.string.update_progress),
						msg.arg1) + "%");
				
				setPointer(info1.kilobits);
				break;
				
			case MSG_UPDATE_CONNECTION_TIME:

				int i = networkType();
				if (i == 0) {
					mTxtNetwork.setText("Network Type: Wifi");
				} else if (i == 1) {
					mTxtNetwork.setText("Network Type: Mobile");
				}
				else {
					mTxtNetwork.setText("Network Type: Unknown");
				}

//				mTxtConnectionSpeed.setText(String.format(getResources()
//						.getString(R.string.update_connectionspeed), msg.arg1));
				break;
				
			case MSG_COMPLETE_STATUS:
				final SpeedInfo info2 = (SpeedInfo) msg.obj;
				mProgressBar.setProgress(100);
				// mTxtProgrNum.setText("100%");
				mTxtProgress.setText(
						String.format(getResources().getString(
								R.string.update_progress), 100) + "%");
				
				setPointer(info2.kilobits);
				
				if (msg.arg2 == TYPE_DOWNLOAD) {
					mTxtSpeed.setText(
							String.format(getResources().getString(R.string.update_downloaded_complete),
								mDecimalFormater.format(info2.kilobits)));
					mBtnStart.setText("Download complete");
					try {
						//Thread.sleep(1000);
						mTestType = TYPE_UPLOAD;
						mBtnStart.setText("Uploading test...");
						setPointer(ZERO_POINT_DEGREE);
						new Thread(mWorker2).start();
					} catch (Exception e) {
						Log.e(TAG, "Handler Exception " + e.getMessage());
					}
				}
				else if (msg.arg2 == TYPE_UPLOAD)
				{
					Log.d(TAG, "" + info2.kilobits);
					mTxtUploadSpeed.setText(String.format("Upload speed: %s kbit/sec", mDecimalFormater.format(info2.kilobits)));
					mBtnStart.setEnabled(true);
					mBtnStart.setText("Start Test");
				}
				break;
				
			default:
				super.handleMessage(msg);
			}
		}
	};

	private final Runnable mWorker = new Runnable() {

		@Override
		public void run() {
			InputStream stream = null;
			try {
				int bytesIn = 0;
				String downloadFileUrl = "http://www.gregbugaj.com/wp-content/uploads/2009/03/dummy.txt";
				long startCon = System.currentTimeMillis();
				URL url = new URL(downloadFileUrl);
				URLConnection con = url.openConnection();
				con.setUseCaches(false);
				long connectionLatency = System.currentTimeMillis() - startCon;
				stream = con.getInputStream();

				Message msgUpdateConnection = Message.obtain(mHandler,
						MSG_UPDATE_CONNECTION_TIME);
				msgUpdateConnection.arg1 = (int) connectionLatency;
				mHandler.sendMessage(msgUpdateConnection);

				long start = System.currentTimeMillis();
				long updateStart = System.currentTimeMillis();
				long updateDelta = 0;
				int bytesInThreshold = 0;
				
				File SDCardRoot = Environment.getExternalStorageDirectory();
		        File file = new File(SDCardRoot,"dummy.txt");
				//this will be used to write the downloaded data into the file we created
		        FileOutputStream fileOutput = new FileOutputStream(file);

		        byte[] buffer = new byte[1];
		        int bufferLength = 0;
		        
				while ((bufferLength = stream.read(buffer)) != -1) {
					fileOutput.write(buffer, 0, bufferLength);
					
					bytesIn++;
					bytesInThreshold++;
					if (updateDelta >= UPDATE_INTERVAL) {
						int progress = (int) ((bytesIn / (double) EXPECTED_SIZE_IN_BYTES) * 100);
						Message msg = Message.obtain(mHandler,
								MSG_UPDATE_STATUS,
								calculate(updateDelta, bytesInThreshold));
						msg.arg1 = progress;
						msg.arg2 = bytesIn;
						mHandler.sendMessage(msg);
						// Reset
						updateStart = System.currentTimeMillis();
						bytesInThreshold = 0;
					}
					updateDelta = System.currentTimeMillis() - updateStart;
				}
				
				fileOutput.close();

				long downloadTime = (System.currentTimeMillis() - start);
				// Prevent AritchmeticException
				if (downloadTime == 0) {
					downloadTime = 1;
				}

				Message msg = Message.obtain(mHandler, MSG_COMPLETE_STATUS,
						calculate(downloadTime, bytesIn));
				msg.arg1 = bytesIn;
				msg.arg2 = TYPE_DOWNLOAD;
				mHandler.sendMessage(msg);
			} catch (MalformedURLException e) {
				Log.e(TAG, "MalformedURLException " + e.getMessage());
			} catch (IOException e) {
				Log.e(TAG, "IOException " + e.getMessage());
			} finally {
				try {
					if (stream != null) {
						stream.close();
					}
				} catch (Exception e) {
					Log.e(TAG, "Exception " + e.getMessage());
				}
			}

		}
	};

	private final Runnable mWorker2 = new Runnable() {
		@Override
		public void run() {
			HttpURLConnection connection = null;
			DataOutputStream outputStream = null;

			String pathToOurFile = Environment.getExternalStorageDirectory().getPath()
					+ "/dummy.txt";
			String urlServer = "http://www.androidexample.com/media/UploadToServer.php";
			String lineEnd = "\r\n";
			String twoHyphens = "--";
			String boundary = "*****";

			int bytesRead, bytesAvailable, bufferSize;
			int totalBytes = 0;
			byte[] buffer;
			int maxBufferSize = 1 * 1024;

			try {
				FileInputStream fileInputStream = new FileInputStream(new File(
						pathToOurFile));

				URL url = new URL(urlServer);
				connection = (HttpURLConnection) url.openConnection();

				// Allow Inputs & Outputs
				connection.setDoInput(true);
				connection.setDoOutput(true);
				connection.setUseCaches(false);

				// Enable POST method
				connection.setRequestMethod("POST");

				connection.setRequestProperty("Connection", "Keep-Alive");
				connection.setRequestProperty("Content-Type",
						"multipart/form-data;boundary=" + boundary);

				outputStream = new DataOutputStream(connection.getOutputStream());
				outputStream.writeBytes(twoHyphens + boundary + lineEnd);
				outputStream
						.writeBytes("Content-Disposition: form-data; name=\"uploadedfile\";filename=\""
								+ pathToOurFile + "\"" + lineEnd);
				outputStream.writeBytes(lineEnd);

				bytesAvailable = fileInputStream.available();
				bufferSize = Math.min(bytesAvailable, maxBufferSize);
				buffer = new byte[bufferSize];

				// Read file
				bytesRead = fileInputStream.read(buffer, 0, bufferSize);

				long up_start = System.currentTimeMillis();
				long up_time = 0;
				
				long updateStart = System.currentTimeMillis();
				long updateDelta = 0;

				while (bytesRead > 0) {
					outputStream.write(buffer, 0, bufferSize);

					totalBytes += bytesRead;
					
					bytesAvailable = fileInputStream.available();
					bufferSize = Math.min(bytesAvailable, maxBufferSize);
					bytesRead = fileInputStream.read(buffer, 0, bufferSize);

//					if (updateDelta >= UPDATE_INTERVAL) {
//						up_time = System.currentTimeMillis() - up_start;
//						//int progress = (int) ((totalBytes / (double) EXPECTED_SIZE_IN_BYTES) * 100);
//						Message msg = Message.obtain(mHandler,
//								MSG_UPDATE_STATUS,
//								calculate(up_time, totalBytes));
//						//msg.arg1 = progress;
//						msg.arg2 = totalBytes;
//						mHandler.sendMessage(msg);
//						// Reset
//						updateStart = System.currentTimeMillis();
//						//bytesInThreshold = 0;
//					}
					updateDelta = System.currentTimeMillis() - updateStart;
					Log.d(TAG, "updateDelta " + updateDelta + "  totalBytes " + totalBytes);
				}

				outputStream.writeBytes(lineEnd);
				outputStream.writeBytes(twoHyphens + boundary + twoHyphens
						+ lineEnd);

				// Responses from the server (code and message)
				int serverResponseCode = connection.getResponseCode();
				String  serverResponseMessage = connection.getResponseMessage();
				Log.d(TAG, "serverResponseCode = " + serverResponseCode + "  "
						+ "serverResponseMessage = " + serverResponseMessage);
				
				up_time = System.currentTimeMillis() - up_start;
				Log.d(TAG, "up_time " + up_time);
				Log.d(TAG, "totalBytes " + totalBytes);
				
				Message msg = Message.obtain(mHandler, MSG_COMPLETE_STATUS,
						calculate(up_time, totalBytes));
				msg.arg1 = totalBytes;
				msg.arg2 = TYPE_UPLOAD;
				mHandler.sendMessage(msg);

				fileInputStream.close();
				outputStream.flush();
				outputStream.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};

	private int networkType() {
		if (Connectivity.isConnectedWifi(getApplicationContext())) {
			return 0;
		} else if (Connectivity.isConnectedMobile(getApplicationContext())) {
			return 1;
		}

		return -1;
	}

	protected void setPointer(double kbps) {
		final int DEGREE_OF_1M = 120;
		float degree = (float)kbps / 1024 * DEGREE_OF_1M;
		
		setPointer(ZERO_POINT_DEGREE + degree);
	}
	
	protected void setPointer(float degree) {
		// create a matrix object
		Matrix matrix = new Matrix();

		// matrix.postRotate(-30, 200, 200);
		matrix.postRotate(degree);

		// create a new bitmap from the original using the matrix to transform
		// the result
		Bitmap rotatedBitmap = Bitmap.createBitmap(myBitmap, 0, 0,
				pointerW, pointerH, matrix, true);

		// display the rotated bitmap
		imgPointer.setImageBitmap(rotatedBitmap);
	}

	/**
	 * 
	 * 1 byte = 0.0078125 kilobits 1 kilobits = 0.0009765625 megabit
	 * 
	 * @param downloadTime
	 *            in miliseconds
	 * @param bytesIn
	 *            number of bytes downloaded
	 * @return SpeedInfo containing current speed
	 */
	private SpeedInfo calculate(final long downloadTime, final long bytesIn) {
		SpeedInfo info = new SpeedInfo();
		// from mil to sec
		long bytespersecond = (bytesIn / downloadTime) * 1000;
		double kilobits = bytespersecond * BYTE_TO_KILOBIT;
		double megabits = kilobits * KILOBIT_TO_MEGABIT;
		info.downspeed = bytespersecond;
		info.kilobits = kilobits;
		info.megabits = megabits;

		return info;
	}

	private static class SpeedInfo {
		public double kilobits = 0;
		public double megabits = 0;
		public double downspeed = 0;
	}

	// Private fields
	private static final String TAG = SpeedTestLauncher.class.getSimpleName();
	private static final int EXPECTED_SIZE_IN_BYTES = 1048576;

	private static final double BYTE_TO_KILOBIT = 0.0078125;
	private static final double KILOBIT_TO_MEGABIT = 0.0009765625;

	private Button mBtnStart;
	private TextView mTxtSpeed;
	//private TextView mTxtConnectionSpeed;
	private TextView mTxtProgress;
	private TextView mTxtNetwork;
	private ProgressBar mProgressBar;
	private TextView mTxtUploadSpeed;

	private final static int MSG_UPDATE_STATUS = 0;
	private final static int MSG_UPDATE_CONNECTION_TIME = 1;
	private final static int MSG_COMPLETE_STATUS = 2;

	private final static int UPDATE_INTERVAL = 200;
	
	private int mTestType;
	private final static int TYPE_DOWNLOAD = 0;
	private final static int TYPE_UPLOAD = 1;
	
	private final static float ZERO_POINT_DEGREE = -85;

	private ImageView imgPointer;
	private Bitmap myBitmap;

	private int pointerW;
	private int pointerH;

	private DecimalFormat mDecimalFormater;

}